/*
 * Copyright 2023-2025 Licensed under the Dual Licensing
 * website: https://aizuda.com
 */
package com.aizuda.bpm.spring.adaptive;

import com.aizuda.bpm.engine.FlowLongExpression;
import com.aizuda.bpm.engine.model.NodeExpression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.List;
import java.util.Map;

/**
 * Spring el表达式解析器
 *
 * <p>
 * <a href="https://aizuda.com">官网</a>尊重知识产权，不允许非法使用，后果自负
 * </p>
 *
 * @author ximu
 * @since 1.0
 */
public class SpelFlowLongExpression implements FlowLongExpression {
    private final ExpressionParser parser;

    public SpelFlowLongExpression() {
        parser = new SpelExpressionParser();
    }

    @Override
    public boolean eval(List<List<NodeExpression>> conditionList, Map<String, Object> args) {
        return this.eval(conditionList, () -> args, expr -> {
            StandardEvaluationContext context = new StandardEvaluationContext();
            context.setVariables(args);
            return parser.parseExpression(expr).getValue(context, Boolean.class);
        });
    }

    @Override
    public String exprOfArgs(NodeExpression nodeExpression, Map<String, Object> args) {
        String value = nodeExpression.getValue();
        String operator = nodeExpression.getOperator();
        String field = nodeExpression.getField();
        if ("include".equalsIgnoreCase(operator)) {
            return "#" + field + ".contains('" + value + "')";
        }
        if ("notinclude".equalsIgnoreCase(operator)) {
            return "not #" + field + ".contains('" + value + "')";
        }
        if ("in".equalsIgnoreCase(operator)) {
            // 处理数组值的in操作符，判断字段值是否在数组中
            String arrayExpr = parseArrayValue(value);
            return "{" + arrayExpr + "}.contains(#" + field + ")";
        }
        if ("notIn".equalsIgnoreCase(operator)) {
            // 处理数组值的notIn操作符，判断字段值是否不在数组中
            String arrayExpr = parseArrayValue(value);
            return "not {" + arrayExpr + "}.contains(#" + field + ")";
        }
        Object fieldValue = args.get(nodeExpression.getField());
        if (fieldValue instanceof String) {
            value = "'" + value + "'";
        }
        return "#" + field + " " + operator + " " + value;
    }

    /**
     * 解析数组值，支持JSON数组格式和逗号分隔格式
     * @param value 数组值字符串
     * @return SpEL表达式可识别的数组格式
     */
    private String parseArrayValue(String value) {
        if (value == null || value.trim().isEmpty()) {
            return "";
        }

        // 处理JSON数组格式 ["value1","value2"]
        if (value.trim().startsWith("[") && value.trim().endsWith("]")) {
            // 移除方括号
            String content = value.trim().substring(1, value.trim().length() - 1);
            // 分割并处理每个元素
            String[] elements = content.split(",");
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < elements.length; i++) {
                String element = elements[i].trim();
                // 移除引号
                if (element.startsWith("\"") && element.endsWith("\"")) {
                    element = element.substring(1, element.length() - 1);
                }
                // 添加单引号用于SpEL表达式
                result.append("'").append(element).append("'");
                if (i < elements.length - 1) {
                    result.append(",");
                }
            }
            return result.toString();
        }

        // 处理已经带单引号的格式 'value1','value2'
        if (value.contains("','")) {
            return value;
        }

        // 处理逗号分隔格式 value1,value2
        String[] elements = value.split(",");
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < elements.length; i++) {
            String element = elements[i].trim();
            // 如果元素已经有单引号，直接使用
            if (element.startsWith("'") && element.endsWith("'")) {
                result.append(element);
            } else {
                result.append("'").append(element).append("'");
            }
            if (i < elements.length - 1) {
                result.append(",");
            }
        }
        return result.toString();
    }
}
